package pt.compflow.interfaces.event;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.StringEntity;

public class RestfulRequest {

	public static final int METHOD_GET = 1;
	public static final int METHOD_POST = 2;
	public static final int METHOD_PUT = 3;
	public static final int METHOD_DELETE = 4;

	private boolean forceOnlyGetAndPost = false;

	private String endpoint;

	private int requestMethod;

	private HashMap<String, Object> urlParams;
	private HashMap<String, String> headerParams;

	private HttpEntity body;

	private boolean isCanceled = false;;

	public RestfulRequest(String endpoint) {
		this.endpoint = endpoint;
		urlParams = new HashMap<String, Object>();
		headerParams = new HashMap<String, String>();

		headerParams.put("Accept", "application/json");
		headerParams.put("Content-Type", "application/json");
		headerParams.put("Cache-Control", "no-cache");
	}

	public RestfulRequest(String endpoint, int requestMethod) {
		this(endpoint);
		this.requestMethod = requestMethod;
	}

	public RestfulRequest(String endpoint, int requestMethod, boolean forceOnlyGetAndPost) {
		this(endpoint, requestMethod);
		this.forceOnlyGetAndPost = forceOnlyGetAndPost;
	}

	/**
	 * 
	 * Builds a Java HttpUriRequest object with all the request data. This
	 * method is invoked asynchronously.
	 * 
	 * @throws Exception
	 *             (possibly encoding)
	 */
	public HttpUriRequest buildRequest() throws Exception {

		HttpUriRequest request = null;

		String combinedParams = "";
		if (!urlParams.isEmpty()) {
			combinedParams += "?";
			for (Map.Entry<String, Object> p : urlParams.entrySet()) {
				String paramString = p.getKey() + "=" + URLEncoder.encode(p.getValue().toString(), "UTF-8");

				if (combinedParams.length() > 1)
					combinedParams += "&";

				combinedParams += paramString;
			}
		}

		switch (requestMethod) {
		case METHOD_GET: {
			request = new HttpGet(endpoint + combinedParams);
			break;
		}
		case METHOD_POST: {
			request = new HttpPost(endpoint + combinedParams);
			if (body != null)
				((HttpPost) request).setEntity(body);
			break;
		}
		case METHOD_PUT: {
			if (!forceOnlyGetAndPost) {
				request = new HttpPut(endpoint + combinedParams);
				if (body != null)
					((HttpPut) request).setEntity(body);
			} else {
				request = new HttpPost(endpoint + combinedParams);
				headerParams.put("X-HTTP-Method-Override", "PUT");
				if (body != null)
					((HttpPost) request).setEntity(body);
			}
			break;
		}
		case METHOD_DELETE: {
			if (!forceOnlyGetAndPost) {
				request = new HttpDelete(endpoint + combinedParams);
			} else {
				request = new HttpPost(endpoint + combinedParams);
				headerParams.put("X-HTTP-Method-Override", "DELETE");
				if (body != null)
					((HttpPost) request).setEntity(body);
			}
			break;
		}
		}

		// add headers
		for (Map.Entry<String, String> h : headerParams.entrySet()) {
			request.addHeader(h.getKey(), h.getValue());
		}

		return request;
	}

	/**
	 * Some servers do not allow HTTP methods other than GET and POST Setting
	 * this flag to true uses the X-HTTP-Method-Override as a work-around
	 */
	public boolean isForceOnlyGetAndPost() {
		return forceOnlyGetAndPost;
	}

	/**
	 * Some servers do not allow HTTP methods other than GET and POST Setting
	 * this flag to true uses the X-HTTP-Method-Override as a work-around
	 * 
	 * @param forceOnlyGetAndPost
	 */
	public void setForceOnlyGetAndPost(boolean forceOnlyGetAndPost) {
		this.forceOnlyGetAndPost = forceOnlyGetAndPost;
	}

	public String getEndpoint() {
		return endpoint;
	}

	public void setEndpoint(String endpoint) {
		this.endpoint = endpoint;
	}

	public HttpEntity getBody() {
		return body;
	}

	public void setBody(HttpEntity body) {
		this.body = body;
	}

	public void setBody(String str) throws UnsupportedEncodingException {
		this.body = new StringEntity(str);
	}

	public HashMap<String, Object> getUrlParams() {
		return urlParams;
	}

	/**
	 * obj.toString() will be used when creating the param String
	 * 
	 * @param key
	 * @param obj
	 */
	public void setUrlParam(String key, Object obj) {
		urlParams.put(key, obj.toString());
	}

	public Object removeUrlParam(String key) {
		if (urlParams.containsKey(key)) {
			urlParams.remove(key);
		}
		return null;
	}

	public HashMap<String, String> getHeaderParams() {
		return headerParams;
	}

	public void setHeaderParam(String key, String value) {
		headerParams.put(key, value);
	}

	public String removeHeaderParam(String key) {
		if (headerParams.containsKey(key)) {
			headerParams.remove(key);
		}
		return null;
	}

	public boolean isCanceled() {
		return isCanceled;
	}

	public void setCanceled(boolean isCanceled) {
		this.isCanceled = isCanceled;
	}

}
